package com.whz.governance.sensitive.engine;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.whz.generic.GarbledStringUtils;
import org.apache.commons.lang.StringUtils;

import java.util.List;

/**
 * 敏感词字典树
 *
 * @author whz
 */
public class SkTrieTree {

    /**
     * 敏感词最大长度
     */
    private int maxLen = 3;

    private final SkTrieNode root = new SkTrieNode('/', false);

    /**
     * 单个敏感词插入trie树
     *
     * @param key 当前敏感词
     */
    public void insertSensitiveWord(String key) {
        if (StringUtils.isBlank(key)) {
            return;
        }
        int len = key.length();

        // 记录敏感词最大长度
        maxLen = Math.max(len, maxLen);

        SkTrieNode cur = root;
        for (int index = 0; index < len; index++) {
            // cur 指向新敏感词节点(若不存在),指向就节点(已存在)
            cur = cur.addChildAndReturnSkNode(key.charAt(index), index == len - 1);
        }
    }

    /**
     * 构建敏感词trie树
     *
     * @param sensitiveWords 敏感词列表
     */
    public void buildTrie(final List<String> sensitiveWords) {
        if (CollUtil.isEmpty(sensitiveWords)) {
            return;
        }
        // 逐个保存
        sensitiveWords.forEach(this::insertSensitiveWord);
    }

    /**
     * 敏感词过滤,返回命中的敏感词列表
     *
     * @param text 待处理文本
     * @return 命中的敏感词列表
     */
    public List<String> filter(final String text) {
        if (StringUtils.isBlank(text)) {
            throw new RuntimeException("text must not blank.");
        }
        List<String> matchWords = Lists.newLinkedList();
        // 最大长度的一半作为敏感词的一般长度
        StringBuilder word = createWordSb();
        SkTrieNode cur = root;
        int len = text.length();
        for (int index = 0; index < len; index++) {
            Character k = text.charAt(index);
            // 过滤掉乱码
            if (GarbledStringUtils.isGarbledCode(k)) {
                continue;
            }

            cur = cur.getChild(k);
            // 当前字符不是敏感词
            if (cur == null) {
                // 文本后移
                cur = root;
            } else {
                // 保存敏感词
                word.append(k);
                // 此处可以设为模糊匹配或者前缀匹配
                if (cur.isComplete()) {
                    // 整个敏感词全部命中
                    matchWords.add(word.toString());
                    // word 清空
                    word = createWordSb();
                }
            }
        }
        return matchWords;
    }

    private StringBuilder createWordSb() {
        return new StringBuilder(maxLen > 2? maxLen >> 1 : 2);
    }
}